// Support functions for blitters that use DirectX
#include "burner.h"
#include "vid_directx_support.h"
#include "vid_support.h"

// ---------------------------------------------------------------------------
// General

static IDirectDraw7* pDD = NULL;

void vidSExit()
{
	pDD = NULL;
}

int vidSInit(IDirectDraw7* pDD7)
{
	pDD = pDD7;
	return pDD ? 0 : 1;
}

// ---------------------------------------------------------------------------

// Get bit-depth of a surface (15/16/24/32)
int vidSGetSurfaceDepth(IDirectDrawSurface7* pSurf)
{
	if (!pSurf) {
		return 0;
	}

	// Find out the pixelformat of the screen surface
	DDPIXELFORMAT ddpf;
	memset(&ddpf, 0, sizeof(ddpf));
	ddpf.dwSize = sizeof(ddpf);

	if (SUCCEEDED(pSurf->GetPixelFormat(&ddpf))) {
		unsigned depth = ddpf.dwRGBBitCount;
		if (depth == 16 && ddpf.dwGBitMask == 0x03E0) {
			depth = 15;
		}
		return depth;
	}
	return 0;
}

// Clear a surface to a specified colour
int vidSClearSurface(IDirectDrawSurface7* pSurf, unsigned int colour, RECT* pRect)
{
	if (!pSurf) {
		return 1;
	}

	DDBLTFX BltFx;
	memset(&BltFx, 0, sizeof(BltFx));
	BltFx.dwSize = sizeof(BltFx);
	BltFx.dwFillColor = colour;

	if (FAILED(pSurf->Blt(NULL, NULL, pRect, DDBLT_COLORFILL, &BltFx))) {
		return 1;
	}
	return 0;
}

// ---------------------------------------------------------------------------
// Fullscreen mode support routines

int vidSScoreDisplayMode(VidSDisplayScoreInfo* scoreInfo)
{
	// Continue if the resolution is too low
	if (scoreInfo->modeWidth < scoreInfo->requestedWidth
		|| scoreInfo->modeHeight < scoreInfo->requestedHeight) {
		return 0;
	}

	// Only test standard resolutions if below 512 x 384
	if ((scoreInfo->modeWidth == 320 && scoreInfo->modeHeight == 240)
		|| (scoreInfo->modeWidth == 400 && scoreInfo->modeHeight == 300)
		|| (scoreInfo->modeWidth >= 512 && scoreInfo->modeHeight >= 384)) {

		if (scoreInfo->requestedWidth == 0 || scoreInfo->requestedHeight == 0) {
			RECT rect = {0, 0, 0, 0};
			unsigned gameWidth = videoInterface.getImageWidth();
			unsigned gameHeight = videoInterface.getImageHeight();
			unsigned int score;

			if (driverOkay) {
				if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && !(vidRotationAdjust & 1)) {
					BurnDrvGetVisibleSize(gameHeight, gameWidth);
				} else {
					BurnDrvGetVisibleSize(gameWidth, gameHeight);
				}
			}

			vidScrnWidth = rect.right = scoreInfo->modeWidth;
			vidScrnHeight = rect.bottom = scoreInfo->modeHeight;
			videoInterface.scale(&rect, gameWidth, gameHeight);

			// Continue if the resolution is too low
			if (!driverOkay) {
				if ((unsigned int)(rect.bottom - rect.top) < scoreInfo->requestedHeight) {
					return 0;
				}
			} else {
				if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
					if ((unsigned int)(rect.right - rect.left) < scoreInfo->requestedWidth) {
						return 0;
					}
				} else {
					if ((unsigned int)(rect.bottom - rect.top) < scoreInfo->requestedHeight) {
						return 0;
					}
				}
			}

			// Score resolutions based on how many pixels are unused and the pixel aspect
			score = 65536 * (scoreInfo->modeWidth - (unsigned)(rect.right - rect.left)) + (scoreInfo->modeHeight - (unsigned)(rect.bottom - rect.top));
			score = (int)((double)score * ((double)scoreInfo->modeWidth / scoreInfo->modeHeight / vidScrnAspect));
			if (score < scoreInfo->bestScore) {
				scoreInfo->bestScore = score;
				scoreInfo->bestWidth = scoreInfo->modeWidth;
				scoreInfo->bestHeight = scoreInfo->modeHeight;
			}
		} else {
			// Select the lowest resolution that will fit the image
			if (scoreInfo->modeWidth < scoreInfo->bestWidth
				&& scoreInfo->modeHeight < scoreInfo->bestHeight) {
				scoreInfo->bestWidth = scoreInfo->modeWidth;
				scoreInfo->bestHeight = scoreInfo->modeHeight;
			}
		}
	}

	return 0;
}

int vidSInitScoreInfo(VidSDisplayScoreInfo* scoreInfo)
{
	scoreInfo->bestWidth = -1U;
	scoreInfo->bestHeight = -1U;
	scoreInfo->bestScore = -1U;

	if (scoreInfo->requestedZoom == 0) {
		return 0;
	}

	scoreInfo->requestedWidth = 0;
	scoreInfo->requestedHeight = 0;

	unsigned gameWidth = videoInterface.getImageWidth();
	unsigned gameHeight = videoInterface.getImageHeight();

	if (driverOkay) {
		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && !(vidRotationAdjust & 1)) {
			BurnDrvGetVisibleSize(gameHeight, gameWidth);
		} else {
			BurnDrvGetVisibleSize(gameWidth, gameHeight);
		}
	}

	if (!vidCorrectAspect || vidFullStretch) {
		scoreInfo->requestedWidth = gameWidth * scoreInfo->requestedZoom;
		scoreInfo->requestedHeight = gameHeight * scoreInfo->requestedZoom;
	} else {
		if (driverOkay) {
			if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
				scoreInfo->requestedWidth = gameWidth * scoreInfo->requestedZoom;
			} else {
				scoreInfo->requestedHeight = gameHeight * scoreInfo->requestedZoom;
			}
		}
	}

	return 0;
}

static HRESULT WINAPI myEnumModesCallback(LPDDSURFACEDESC2 pSurfaceDesc, void* lpContext)
{
	((VidSDisplayScoreInfo*)lpContext)->modeWidth = pSurfaceDesc->dwWidth;
	((VidSDisplayScoreInfo*)lpContext)->modeHeight = pSurfaceDesc->dwHeight;

	vidSScoreDisplayMode((VidSDisplayScoreInfo*)lpContext);

	return DDENUMRET_OK;
}

void vidSRestoreScreenMode()
{
	if (!pDD) {
		return;
	}

	// Undo the changes we made to the display
	pDD->SetCooperativeLevel(NULL, DDSCL_NORMAL);

	vidScrnWidth = 0;
	vidScrnHeight = 0;
}

// Enter fullscreen mode, select optimal full-screen resolution
int vidSEnterFullscreenMode(int zoom, unsigned depth)
{
	if (!pDD) {
		return 1;
	}

	if (depth == 0) {
		depth = vidDepth;
	}
	if (depth == 15) {
		depth = 16;
	}

	if (FAILED(pDD->SetCooperativeLevel(hVidWnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN))) {
		return 1;
	}

	int nWidth = vidWidth, nHeight = vidHeight;

	if (zoom) {
		VidSDisplayScoreInfo ScoreInfo;
		memset(&ScoreInfo, 0, sizeof(VidSDisplayScoreInfo));
		ScoreInfo.requestedZoom = zoom;
		vidSInitScoreInfo(&ScoreInfo);

		DDSURFACEDESC2 ddsd;
		memset(&ddsd, 0, sizeof(ddsd));
		ddsd.dwSize = sizeof(ddsd);
		ddsd.dwFlags = DDSD_PIXELFORMAT;

		ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
		ddsd.ddpfPixelFormat.dwRGBBitCount = depth;

		pDD->EnumDisplayModes(0, &ddsd, (void*)&ScoreInfo, myEnumModesCallback);

		if (ScoreInfo.bestWidth == -1U) {
			vidSRestoreScreenMode();
			FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_NOMODE));
			FBAPopupDisplay(PUF_TYPE_ERROR);
			return 1;
		}

		nWidth = ScoreInfo.bestWidth;
		nHeight = ScoreInfo.bestHeight;
	}

	if (!driverOkay && (nWidth < 640 || nHeight < 480)) {
		return 1;
	}

	if (FAILED(pDD->SetDisplayMode(nWidth, nHeight, depth, vidRefresh, 0))) {
		vidSRestoreScreenMode();

		FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_PROBLEM), nWidth, nHeight, depth, vidRefresh);
		if (nWidth != 320 && nHeight != 240) {
			FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_CUSTRES));
		}
		FBAPopupDisplay(PUF_TYPE_ERROR);

		vidScrnWidth = 0;
		vidScrnHeight = 0;

		return 1;
	}

	vidScrnWidth = nWidth;
	vidScrnHeight = nHeight;

	return 0;
}

// ---------------------------------------------------------------------------
// Text display routines

#define MESSAGE_SIZE 64
// short message
static struct {
	wchar_t pMsgText[MESSAGE_SIZE];
	COLORREF colour;
	int priority;
	unsigned int nTimer;
} VidSShortMsg = { L"", 0, 0, 0,};
static HFONT shortMsgFont = NULL;
const int shortMsgWidth = 240;

// tiny message
static struct {
	wchar_t pMsgText[MESSAGE_SIZE];
	COLORREF colour;
	int priority;
	unsigned int nTimer;
} VidSTinyMsg = {L"", 0, 0, 0};
static HFONT tinyMsgFont = NULL;
const int tinyMsgWidth = 300;

// ddraw surface
static IDirectDrawSurface7* pShortMsgSurf = NULL;
static IDirectDrawSurface7* pTinyMsgSurf = NULL;

static unsigned int keyColour = 0x000001;
static int zoom;				// & 1: zoom X, & 2: zoom Y

wchar_t OSDMsg[128];
unsigned int OSDTimer = 0;
wchar_t tinyMsg[64];
unsigned int tinyTimer = 0;

static BOOL myTextOut(HDC hdc, int xStart, int yStart, LPCTSTR lpString, int cbString, int shadowOffset, int colour)
{
	// Print a black shadow
	SetTextColor(hdc, 0);
	if (shadowOffset >= 2) {
		TextOut(hdc, xStart + shadowOffset, yStart + shadowOffset, lpString, cbString);
	}
	// Print a black outline
	TextOut(hdc, xStart - 1, yStart - 1, lpString, cbString);
	TextOut(hdc, xStart + 0, yStart - 1, lpString, cbString);
	TextOut(hdc, xStart + 1, yStart - 1, lpString, cbString);
	TextOut(hdc, xStart + 1, yStart + 0, lpString, cbString);
	TextOut(hdc, xStart + 1, yStart + 1, lpString, cbString);
	TextOut(hdc, xStart + 0, yStart + 1, lpString, cbString);
	TextOut(hdc, xStart - 1, yStart + 1, lpString, cbString);
	TextOut(hdc, xStart - 1, yStart + 0, lpString, cbString);
	// Print the text on top
	SetTextColor(hdc, colour);

	return TextOut(hdc, xStart, yStart, lpString, cbString);
}

static BOOL myExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT* lprc, LPCTSTR lpString, UINT count, CONST INT* lpDx, int shadowOffset, int colour)
{
	// Print a black shadow
	SetTextColor(hdc, 0);
	if (shadowOffset >= 2) {
		ExtTextOut(hdc, X + shadowOffset, Y + shadowOffset, fuOptions, lprc, lpString, count, lpDx);
	}
	// Print a black outline
	ExtTextOut(hdc, X - 1, Y - 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X + 0, Y - 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X + 1, Y - 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X + 1, Y + 0, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X + 1, Y + 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X + 0, Y + 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X - 1, Y + 1, fuOptions, lprc, lpString, count, lpDx);
	ExtTextOut(hdc, X - 1, Y + 0, fuOptions, lprc, lpString, count, lpDx);
	// Print the text on top
	SetTextColor(hdc, colour);

	return ExtTextOut(hdc, X, Y, fuOptions, lprc, lpString, count, lpDx);
}

static void VidSExitTinyMsg()
{
	VidSTinyMsg.nTimer = 0;

	if (tinyMsgFont) {
		DeleteObject(tinyMsgFont);
		tinyMsgFont = NULL;
	}

	RELEASE(pTinyMsgSurf)
}

static void VidSExitShortMsg()
{
	VidSShortMsg.nTimer = 0;

	if (shortMsgFont) {
		DeleteObject(shortMsgFont);
		shortMsgFont = NULL;
	}

	RELEASE(pShortMsgSurf)
}

void vidSExitOSD()
{
	VidSExitTinyMsg();
	VidSExitShortMsg();
}

static int VidSInitTinyMsg(int flags)
{
	DDSURFACEDESC2 ddsd;

	VidSExitTinyMsg();

	tinyMsgFont = CreateFont(11 - flags, 0, 0, 0, FW_SEMIBOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_DONTCARE, L"MS Sans Serif");
	VidSTinyMsg.nTimer = 0;

	// create surface to display the text
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;

	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;

	ddsd.dwWidth = tinyMsgWidth;
	ddsd.dwHeight = 20;

	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = keyColour;
	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = keyColour;

	if (FAILED(pDD->CreateSurface(&ddsd, &pTinyMsgSurf, NULL))) {
		printf("  * Error: Couldn't create OSD texture.\n");
		return 1;
	}

	vidSClearSurface(pTinyMsgSurf, keyColour, NULL);

	return 0;
}

static int VidSInitShortMsg(int flags)
{
	DDSURFACEDESC2 ddsd;

	VidSExitShortMsg();

	shortMsgFont = CreateFont(20 - flags, 0, 0, 0, FW_SEMIBOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_DONTCARE, L"Lucida");
	VidSShortMsg.nTimer = 0;

	// create surface to display the text
	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;

	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;

	ddsd.dwWidth = shortMsgWidth;
	ddsd.dwHeight = 32;

	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = keyColour;
	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = keyColour;

	if (FAILED(pDD->CreateSurface(&ddsd, &pShortMsgSurf, NULL))) {
		dprintf(("  * Error: Couldn't create OSD texture.\n"));
		return 1;
	}

	vidSClearSurface(pShortMsgSurf, keyColour, NULL);

	return 0;
}

int vidSInitOSD(int flags)
{
	if (pDD == NULL) {
		return 1;
	}

	if (VidSInitShortMsg(flags)) {
		return 1;
	}
	if (VidSInitTinyMsg(flags)) {
		return 1;
	}

	return 0;
}

int vidSRestoreOSD()
{
	if (pShortMsgSurf) {
		if (FAILED(pShortMsgSurf->IsLost())) {
			if (FAILED(pShortMsgSurf->Restore())) {
				return 1;
			}
			vidSClearSurface(pShortMsgSurf, keyColour, NULL);

			VidSShortMsg.nTimer = 0;
		}
	}

	if (pTinyMsgSurf) {
		if (FAILED(pTinyMsgSurf->IsLost())) {
		if (FAILED(pTinyMsgSurf->Restore())) {
				return 1;
			}
			vidSClearSurface(pTinyMsgSurf, keyColour, NULL);

			VidSTinyMsg.nTimer = 0;
		}
	}

	return 0;
}

static void VidSDisplayTinyMsg(IDirectDrawSurface7* pSurf, RECT* pRect)
{
	if (VidSTinyMsg.nTimer) {
		RECT src = { 0, 0, tinyMsgWidth, 20 };
		RECT dest = { pRect->right - 320, pRect->bottom - 24, pRect->right - 8, pRect->bottom - 4 };

		// Switch off message display when the message has been displayed long enough
		if (GetFramesEmulated() > VidSTinyMsg.nTimer) {
			VidSTinyMsg.nTimer = 0;
		}

		if (dest.left < pRect->left) {
			src.left = pRect->left - dest.left;
			dest.left = pRect->left;
		}

		if (zoom & 2) {
			dest.top <<= 1;
			dest.bottom <<= 1;
		}

		if (zoom & 1) {
			dest.left <<= 1;
			dest.right <<= 1;
		}

		// Blit the message to the surface using a colourkey
		pSurf->Blt(&dest, pTinyMsgSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
	}
}

static void VidSDisplayShortMsg(IDirectDrawSurface7* pSurf, RECT* pRect)
{
	if (VidSShortMsg.nTimer) {
		RECT src = { 0, 0, shortMsgWidth, 32 };
		RECT dest = { 0, pRect->top + 4, pRect->right - 8, pRect->top + 36 };

		// Switch off message display when the message has been displayed long enough
		if (GetFramesEmulated() > VidSShortMsg.nTimer) {
			VidSShortMsg.nTimer = 0;
		}

		dest.left = dest.right - shortMsgWidth;
		if (dest.left < pRect->left) {
			src.left = pRect->left - dest.left;
			dest.left = pRect->left;
		}

		if (zoom & 2) {
			dest.top <<= 1;
			dest.bottom <<= 1;
		}

		if (zoom & 1) {
			dest.left <<= 1;
			dest.right <<= 1;
		}

		// Blit the message to the surface using a colourkey
		pSurf->Blt(&dest, pShortMsgSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
	}
}

void vidSDisplayOSD(IDirectDrawSurface7* pSurf, RECT* rect, int flags)
{
	zoom = flags & 3;

	VidSDisplayTinyMsg(pSurf, rect);
	VidSDisplayShortMsg(pSurf, rect);
}

int vidSNewTinyMsg(const wchar_t* text, int rgb, int duration, int priority)	// int rgb = 0, int duration = 0, int priority = 5
{
	// If a message with a higher priority is being displayed, exit.
	if (!text || (VidSTinyMsg.nTimer && VidSTinyMsg.priority > priority)) {
		return 1;
	}

	size_t textSize = wcslen(text);
	if (textSize >= MESSAGE_SIZE) {
		textSize = MESSAGE_SIZE - 1;
	}
	wcsncpy(VidSTinyMsg.pMsgText, text, textSize);
	VidSTinyMsg.pMsgText[textSize] = 0;

	// copy tiny message
	memset(tinyMsg, '\0', sizearray(tinyMsg));
	wcsncpy(tinyMsg, text, textSize);

	if (rgb) {
		// Convert RGB value to COLORREF
		VidSTinyMsg.colour = RGB((rgb >> 16), ((rgb >> 8) & 0xFF), (rgb & 0xFF));
	} else {
		// Default message colour (yellow)
		VidSTinyMsg.colour = RGB(0xFF, 0xFF, 0x7F);
	}
	if (duration) {
		VidSTinyMsg.nTimer = GetFramesEmulated() + duration;
	} else {
		VidSTinyMsg.nTimer = GetFramesEmulated() + 120;
	}
	tinyTimer = VidSTinyMsg.nTimer;
	VidSTinyMsg.priority = priority;

	{
		if (!pTinyMsgSurf) {
			return 1;
		}

		// Print the message
		HDC hDC;
		HFONT hFont;

		// Clear the surface first
		vidSClearSurface(pTinyMsgSurf, keyColour, NULL);

		pTinyMsgSurf->GetDC(&hDC);
		SetBkMode(hDC, TRANSPARENT);
		hFont = (HFONT)SelectObject(hDC, tinyMsgFont);
		SetTextAlign(hDC, TA_BOTTOM | TA_RIGHT);

		// Print a black shadow
		SetTextColor(hDC, 0);
		TextOut(hDC, tinyMsgWidth, 20, VidSTinyMsg.pMsgText, (int)wcslen(VidSTinyMsg.pMsgText));
		// Print the text on top
		SetTextColor(hDC, VidSTinyMsg.colour);
		TextOut(hDC, tinyMsgWidth - 1, 20 - 1, VidSTinyMsg.pMsgText, (int)wcslen(VidSTinyMsg.pMsgText));

		// Clean up
		SelectObject(hDC, hFont);
		pTinyMsgSurf->ReleaseDC(hDC);
	}

	return 0;
 }

int vidSNewShortMsg(const wchar_t* text, int rgb, int duration, int priority)	// int rgb = 0, int duration = 0, int priority = 5
{
	// If a message with a higher priority is being displayed, exit.
	if (!text || (VidSShortMsg.nTimer && VidSShortMsg.priority > priority)) {
		return 1;
	}

	size_t textSize = wcslen(text);
	if (textSize >= MESSAGE_SIZE) {
		textSize = MESSAGE_SIZE - 1;
	}
	wcsncpy(VidSShortMsg.pMsgText, text, textSize);
	VidSShortMsg.pMsgText[textSize] = 0;

	// copy osd message
	memset(OSDMsg, '\0', sizearray(OSDMsg));
	wcsncpy(OSDMsg, text, textSize);

	if (rgb) {
		// Convert RGB value to COLORREF
		VidSShortMsg.colour = RGB((rgb >> 16), ((rgb >> 8) & 0xFF), (rgb & 0xFF));
	} else {
		// Default message colour (yellow)
		VidSShortMsg.colour = RGB(0xFF, 0xFF, 0x7F);
	}
	if (duration) {
		VidSShortMsg.nTimer = GetFramesEmulated() + duration;
	} else {
		VidSShortMsg.nTimer = GetFramesEmulated() + 120;
	}
	OSDTimer = VidSShortMsg.nTimer;
	VidSShortMsg.priority = priority;

	{
		if (!pShortMsgSurf) {
			return 1;
		}

		// Print the message
		HDC hDC;
		HFONT hFont;

		// Clear the surface first
		vidSClearSurface(pShortMsgSurf, keyColour, NULL);

		pShortMsgSurf->GetDC(&hDC);
		SetBkMode(hDC, TRANSPARENT);
		hFont = (HFONT)SelectObject(hDC, shortMsgFont);
		SetTextAlign(hDC, TA_TOP | TA_RIGHT);

		myTextOut(hDC, shortMsgWidth - 2, 0, VidSShortMsg.pMsgText, (int)wcslen(VidSShortMsg.pMsgText), 2, VidSShortMsg.colour);

		// Clean up
		SelectObject(hDC, hFont);
		pShortMsgSurf->ReleaseDC(hDC);
	}

	return 0;
}

void vidSKillShortMsg()
{
	VidSShortMsg.nTimer = 0;
}

void vidSKillTinyMsg()
{
	VidSTinyMsg.nTimer = 0;
}

void vidSKillOSDMsg()
{
	OSDTimer = 0;
}

void vidSKillTinyOSDMsg()
{
	tinyTimer = 0;
}
